// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: implementation of  CImplActionDuplicate.
//   $Id$

 
#include "stdafx.h"
#include "implactionduplicate.h"
#include "XFormsElementDuplicate.h"

#include "implBind.h"
#include "model.h"
#include "singlenodebindingondemand.h"
#include "GetEnclosingBindingOf.h"
#include "ImplInstance.h"
CImplActionDuplicate::CImplActionDuplicate(CXFormsElementDuplicate * pXFormsElement)
:m_pBind(0)
,m_pRefEvaluator(0)
,m_pOriginEvaluator(0)
,m_pBeforeEvaluator(0)
{

}

CImplActionDuplicate::~CImplActionDuplicate(void)
{
	delete m_pRefEvaluator;
	delete  m_pOriginEvaluator;
	delete m_pBeforeEvaluator;
	m_pRefEvaluator = m_pOriginEvaluator = m_pBeforeEvaluator = 0;
}

HRESULT CImplActionDuplicate::PerformAction()
{
	HRESULT hr = S_FALSE;

	CComPtr<IXMLDOMNode> pContext;		
	GetXPathContext(&pContext);


	CComPtr<IXMLDOMNode> pTargetParent;
	GetTargetParent(pContext,&pTargetParent);
	//The node which will get the cloned subtree as a child or attribute is determined by
	//the Single Node Binding attributes, this will be the parent node. If the parent node 
	//is not a root node or an element node then the action has no effect.

	if(pTargetParent)
	{
		CComPtr<IXMLDOMNode> pNodeToInsert;
		GetNodeToInsert(pContext,&pNodeToInsert);
		if(pNodeToInsert)
		{
			CComPtr<IXMLDOMNode> pInsertBefore;
			GetInsertLocation(pTargetParent,&pInsertBefore);
			
			hr = insertNode(pTargetParent,pNodeToInsert,pInsertBefore);
		
			if(S_OK == hr) 
			{
				// After the node has been successfully inserted into the xf:instance 
				// we need to dispatch the xforms-duplicate event on the xf:instance. 
				CImplModel * pModel = NULL;
				if(m_pXForms)
					m_pXForms->findModel(pNodeToInsert, &pModel);

				if(pModel) {
					// Locate the xf:instance that the node is stored
					CImplInstance * pInst = NULL;
					pModel->findInstance(pNodeToInsert, &pInst);
					// And dispatch the xforms-duplicate event on that xf:instance
					if(pInst) 
						pInst->FireEvent(EVENT_DUPLICATE);
					// Need to force a rebuild on the model which was modifed.
					pModel->rebuild(m_pXForms->IsInDeferredUpdate());
				}
			}
			else
			{
				hr = S_FALSE;
			}
			
		}
	}
	
	return hr;
}


HRESULT CImplActionDuplicate::GetInsertLocation(IXMLDOMNode * pEvaluationContext,IXMLDOMNode ** ppNodeToInsertBefore)
{
	HRESULT hr;
	if(m_pBeforeEvaluator)
	{
		hr = m_pBeforeEvaluator->GetNode(pEvaluationContext,ppNodeToInsertBefore);
	}
	else
	{
		*ppNodeToInsertBefore = 0;
		hr = S_OK;
	}
	return hr;
}

HRESULT CImplActionDuplicate::GetNodeToInsert(IXMLDOMNode * pEvaluationContext,IXMLDOMNode ** ppNodeToInsert)
{
	HRESULT hr;
	if(m_pOriginEvaluator)
	{
		CComPtr<IXMLDOMNode> pTemplateNode;
		m_pOriginEvaluator->GetNode(pEvaluationContext,&pTemplateNode);
        if(pTemplateNode)
		{
			hr = pTemplateNode->cloneNode(VARIANT_TRUE,ppNodeToInsert);
		}
		else
		{
			*ppNodeToInsert = 0;
			hr = S_FALSE;
		}
	}
	else
	{
		hr = E_PENDING;
	}
	return hr;

}

HRESULT CImplActionDuplicate::GetTargetParent(IXMLDOMNode * pContext, IXMLDOMNode ** ppTargetParent)
{
	HRESULT hr;

	// Bug 745 - If the xf:duplicate is conatined in a xf:model it will be processed 
	// before the actual xf:model, in which case so of these will be null.
	if(!m_pContextParent || !m_pModel || !(m_pRefEvaluator && m_pBind) || m_pOriginEvaluator)
		ResolveBindings();
	
	if(m_pBind)
	{
		hr = m_pBind->GetNodeAt(0,ppTargetParent);
	}
	else if(m_pRefEvaluator)
	{
		CComPtr<IXMLDOMNode> pContext;		
		GetXPathContext(&pContext);

		hr = m_pRefEvaluator->GetNode(pContext,ppTargetParent);
	}
	else
	{
		hr = E_FAIL;
	}
	return hr;
}


HRESULT CImplActionDuplicate::insertNode(IXMLDOMNode * pTargetParent,IXMLDOMNode * pNodeToInsert,IXMLDOMNode * pInsertBefore) const
{	
	HRESULT hr = S_FALSE;
	
	CComQIPtr<IXMLDOMAttribute> pAttr = pNodeToInsert;
	if(pAttr)
	{
		bool bShouldAttributeBeInserted = true;

		if(pInsertBefore)
		{
			DOMNodeType type;
			pInsertBefore->get_nodeType(&type);
			if(NODE_ATTRIBUTE != type)
			{
				CComPtr<IXMLDOMNode> pFirstChildOfParent;
				pTargetParent->get_firstChild(&pFirstChildOfParent);
		
				//if pInsertBefore is not the firstChild of the parent,
				//	then the attribute cannot be inserted directly before it.
				//If there is no first child, and insertBefore is set, then 
				//	the insertion cannot go ahead anyway.
				if(!pFirstChildOfParent || !pFirstChildOfParent.IsEqualObject(pInsertBefore))
				{
					//Can't insert an attribute before any non-attribute
					//	that isn't the firstchild.
					hr = E_INVALIDARG;
					bShouldAttributeBeInserted = false;
				}
			}
		}

		CComQIPtr<IXMLDOMElement> pEl = pTargetParent;
		if(bShouldAttributeBeInserted && pEl)
		{
			CComBSTR sName;
			pAttr->get_name(&sName);
			CComVariant vt;
			hr = pEl->getAttribute(sName,&vt);
			if(S_OK == hr)
			{
				// Bug 792 - If the target is a attribute which already exists then replace 
				// its value with the value of the source attribute.
				CComPtr<IXMLDOMAttribute> pTargetAttr;
				hr = pEl->getAttributeNode(sName, &pTargetAttr);
				if(pTargetAttr) {
					CComVariant vtValue;
					hr = pAttr->get_value(&vtValue);
					if(SUCCEEDED(hr))
						hr = pTargetAttr->put_value(vtValue);
				}
			}
			else
			{
				hr = pEl->setAttributeNode(pAttr,0);
			}
		}
	}
	else
	{
		// Bug 788 - Need to rename xsi:type attributes as they cause the insertBefore()
		// to fail.
		if(m_pModel)
			m_pModel->removeXSITypeAttributes(pNodeToInsert);
		//Don't bother testing, if pInsertBefore ==  NULL, then append occurs as required
		//if it is not a child, or an invalid document would result, MSXML can handle not inserting it.
		hr = pTargetParent->insertBefore(pNodeToInsert,CComVariant(pInsertBefore),0);
		// Bug 788 - Once its been inserted the xsi:type attributes can be added again.
		if(m_pModel)
			m_pModel->replaceXSITypeAttributes(pNodeToInsert);
	}
	return hr;
}
	

void CImplActionDuplicate::ResolveBindings() 
{
	ResolveEvaluationContext();
	//bind takes precedence
	if(m_sBind)
	{	
		if(m_pXForms)
			m_pXForms->getBind(m_sBind,m_pBind);
	}
	else if(m_sRef)
	{
		if(m_pModel && !m_pRefEvaluator)
			m_pRefEvaluator = new CSingleNodeBindingOnDemand(m_pModel,m_sRef);
	}

	if(m_pModel)
	{
		if(m_sOrigin && !m_pOriginEvaluator)
		{
			m_pOriginEvaluator = new CSingleNodeBindingOnDemand(m_pModel,m_sOrigin);
		}
		if(m_sBefore && !m_pBeforeEvaluator)
		{
			m_pBeforeEvaluator = new CSingleNodeBindingOnDemand(m_pModel,m_sBefore);
		}
	}
	
}